/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.gen.plugin;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.star.framework.core.Constant;
import cn.star.framework.gen.Generator;
import cn.star.framework.gen.core.AbstractJavaCodeModel;
import cn.star.framework.gen.core.CodeType;
import cn.star.framework.gen.model.ControllerModel;
import cn.star.framework.gen.model.EntityModel;
import cn.star.framework.gen.model.RepositoryModel;
import cn.star.framework.gen.model.ServiceImplModel;
import cn.star.framework.gen.model.ServiceModel;
import java.util.HashMap;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.fusesource.jansi.Ansi.Attribute;
import org.fusesource.jansi.Ansi.Color;

/**
 * 默认快捷生成<br>
 * 支持输出到文件目录<br>
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
@Slf4j
public class EntityGenerator extends Generator {
  @Getter @Setter private EntityModel entityModel;
  @Getter @Setter private RepositoryModel repositoryModel;
  @Getter @Setter private ServiceModel serviceModel;
  @Getter @Setter private ServiceImplModel serviceImplModel;
  @Getter @Setter private ControllerModel controllerModel;

  private Map<CodeType, AbstractJavaCodeModel> javaCodeMap;

  public EntityGenerator(EntityModel entityModel) {
    this(entityModel, false);
  }

  public EntityGenerator(EntityModel entityModel, boolean license) {
    super(license);

    String entityName = entityModel.getName();
    String repositoryName = StrUtil.format("{}Repository", entityName);
    String serviceName = StrUtil.format("{}Service", entityName);
    String serviceImplName = StrUtil.format("{}ServiceImpl", entityName);
    String controllerName = StrUtil.format("{}Controller", entityName);

    this.entityModel = entityModel;

    this.repositoryModel = new RepositoryModel(repositoryName);
    this.serviceModel = new ServiceModel(serviceName);
    this.serviceImplModel = new ServiceImplModel(serviceImplName);
    this.controllerModel = new ControllerModel(controllerName);

    this.init(entityModel.getBasePackage());
  }

  private void init(String basePackage) {
    this.javaCodeMap = new HashMap<>(5);

    this.repositoryModel.setBasePackage(basePackage);
    this.serviceModel.setBasePackage(basePackage);
    this.serviceImplModel.setBasePackage(basePackage);
    this.controllerModel.setBasePackage(basePackage);

    this.repositoryModel.entity(entityModel);
    this.serviceModel.entity(entityModel);
    this.serviceImplModel.entity(entityModel).repository(repositoryModel).service(serviceModel);
    this.controllerModel
        .entity(entityModel)
        .service(serviceModel)
        .rest(StrUtil.format("/{}", entityModel.getName().toLowerCase()));

    this.javaCodeMap.put(CodeType.ENTITY, entityModel);
    this.javaCodeMap.put(CodeType.REPOSITORY, repositoryModel);
    this.javaCodeMap.put(CodeType.SERVICE, serviceModel);
    this.javaCodeMap.put(CodeType.SERVICE_IMPL, serviceImplModel);
    this.javaCodeMap.put(CodeType.CONTROLLER, controllerModel);
  }

  /**
   * 生成代码且输出到文件目录
   *
   * @param dir 输出目录
   */
  public void gen(String dir) {
    for (CodeType type : CodeType.values()) {
      this.write(dir, type);
    }
  }

  /**
   * 生成代码且输出到文件目录
   *
   * @param dir 输出目录
   * @param types 指定类型{@link CodeType}
   */
  public void gen(String dir, CodeType... types) {
    if (ArrayUtil.isNotEmpty(types)) {
      for (CodeType type : types) {
        this.write(dir, type);
      }
    } else {
      this.gen(dir);
    }
  }

  /**
   * 生成代码
   *
   * @param type {@link CodeType}
   * @return
   */
  public String gen(CodeType type) {
    if (type == CodeType.ENTITY) {
      return this.getEntityCode(entityModel);
    } else if (type == CodeType.REPOSITORY) {
      return this.getRepositoryCode(repositoryModel);
    } else if (type == CodeType.SERVICE) {
      return this.getServiceCode(serviceModel);
    } else if (type == CodeType.SERVICE_IMPL) {
      return this.getServiceImplCode(serviceImplModel);
    } else if (type == CodeType.CONTROLLER) {
      return this.getControllerCode(controllerModel);
    } else {
      throw new RuntimeException("Not type: " + type);
    }
  }

  private void write(String dir, CodeType type) {
    try {
      String code = this.gen(type);
      AbstractJavaCodeModel model = this.javaCodeMap.get(type);
      String packageName = model.getPackageName();
      String name = model.getName();

      FileUtil.writeUtf8String(
          code, StrUtil.format("{}/{}/{}.java", dir, packageName.replaceAll("\\.", "/"), name));
      if (log.isDebugEnabled()) {
        log.debug(
            "\n"
                + Constant.getAnsi(Color.MAGENTA)
                    .bold()
                    .a(Attribute.UNDERLINE)
                    .a("Template(" + type + ") Generator Code ➘\n")
                    .reset()
                + Constant.getAnsi(Color.CYAN).a(code).reset());
      }
    } catch (Exception e) {
      log.warn("generator(write) error, {}", e.getMessage());
    }
  }
}
